<?php
declare(strict_types=1);

namespace App\Helper;

use App\Annotation\ModuleAndAction;
use Hyperf\Di\Annotation\AnnotationCollector;

class ModuleHelper
{
    private static $prepared = false;
    private static $moduleToActions = [];
    private static $allModuleAndActions = [];
    private static $classWithMethodToModuleWithAction = [];
    private static $moduleWithActionToClassAndMethod = [];
    private static $moduleToRoute = [];
    private static $moduleToIcon = [];

    public static function getModuleWithActionByClassAndMethod(?string $class, ?string $method): ?string
    {
        self::prepare();
        $class || $class = '';
        $method || $method = '';
        return self::$classWithMethodToModuleWithAction[$class . '@' . $method] ?? null;
    }

    private static function prepare(): void
    {
        if (self::$prepared) {
            return;
        }
        $classes = AnnotationCollector::getClassesByAnnotation(ModuleAndAction::class);
        /* @var ModuleAndAction $obj */
        foreach ($classes as $class => $obj) {
            $module = $obj->module;
            $actions = $obj->actions;
            $icon = $obj->icon;
            self::setModuleToIcon($module, $icon);
            foreach ($actions as $method => $action) {
                self::setModuleToActions($module, $action);
                self::setClassWithMethodToModuleWithAction($class, $method, $module, $action);
                if (in_array($method, ['list', 'index'], true)) {
                    self::setModuleToRoute($module, RouteHelper::getRouteByControllerAndMethod($class, $method));
                }
            }
        }
        $methods = AnnotationCollector::getMethodsByAnnotation(ModuleAndAction::class);
        foreach ($methods as $item) {
            $class = $item['class'] ?? '';
            $method = $item['method'] ?? '';
            /* @var ModuleAndAction $item ['annotation'] */
            $module = $item['annotation']->module;
            $action = $item['annotation']->action;
            $icon = $item['annotation']->icon;
            if (!empty($class) && !empty($method) && !empty($module) && !empty($action)) {
                self::setModuleToIcon($module, $icon);
                self::setModuleToActions($module, $action);
                self::setClassWithMethodToModuleWithAction($class, $method, $module, $action);
                if (in_array($method, ['list', 'index'], true)) {
                    self::setModuleToRoute($module, RouteHelper::getRouteByControllerAndMethod($class, $method));
                }
            }
        }
        ksort(self::$moduleToActions, SORT_NATURAL);
        self::$prepared = true;
    }

    private static function setModuleToIcon(string $module, string $icon): void
    {
        empty($icon) || self::$moduleToIcon[$module] = $icon;
    }

    private static function setModuleToActions(string $module, string $action): void
    {
        key_exists($module, self::$moduleToActions) || self::$moduleToActions[$module] = [];
        if (!in_array($action, self::$moduleToActions[$module], true)) {
            array_push(self::$moduleToActions[$module], $action);
        }
    }

    private static function setClassWithMethodToModuleWithAction(
        string $class,
        string $method,
        string $module,
        string $action
    ): void {
        self::$classWithMethodToModuleWithAction[$class . '@' . $method] = $module . '@' . $action;
        self::$moduleWithActionToClassAndMethod[$module . '@' . $action] = $class . '@' . $method;
    }

    private static function setModuleToRoute(string $module, ?string $route): void
    {
        is_null($route) || self::$moduleToRoute[$module] = $route;
    }

    public static function getHrefByModule(string $module): string
    {
        self::prepare();
        $route = self::$moduleToRoute[$module] ?? null;
        if (!$route) {
            return '404.html';
        }
        return 'page' . $route . '.html';
    }

    public static function checkAvailable(array $moduleWithAction): array
    {
        self::prepare();
        $availableModuleWithAction = [];
        foreach ($moduleWithAction as $userModuleWithAction) {
            if (key_exists($userModuleWithAction, self::$moduleWithActionToClassAndMethod)) {
                array_push($availableModuleWithAction, $userModuleWithAction);
            }
        }
        return $availableModuleWithAction;
    }

    public static function getAllModules(): array
    {
        self::prepare();
        return array_keys(self::$moduleToActions);
    }

    public static function getAllModuleAndActions(): array
    {
        if (self::$allModuleAndActions) {
            return self::$allModuleAndActions;
        }
        self::prepare();
        $all = [];
        foreach (self::$moduleToActions as $module => $actions) {
            array_push($all, [
                'module' => $module,
                'actions' => $actions,
            ]);
        }
        self::$allModuleAndActions = $all;
        return $all;
    }

    public static function getIconByModule(string $module): string
    {
        self::prepare();
        $icon = self::$moduleToIcon[$module] ?? '';
        empty($icon) && $icon = 'fa fa-list-alt';
        return $icon;
    }
}
